#pragma once
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#include "log.hpp"

Log lg;
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10;

enum{
    CREAT_ERR = 1,
    BIND_ERR,
    LISTEN_ERR,
    USAGE_ERR,
    ACCEPT_ERR
};

class TcpServer;

class ThreadData{
public:
    ThreadData(int fd, const std::string& ip, const uint16_t& port, TcpServer* t)
    :sockfd(fd),
    clientip(ip),
    clientport(port),
    svr(t)
    {}
public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer* svr;
};

class TcpServer{
public:
    TcpServer(const uint16_t& port, const std::string& ip = defaultip)
    :_listensockfd(defaultfd),
    _port(port),
    _ip(ip)
    {}
    ~TcpServer()
    {}

    void Init()
    {
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0); // 流式
        if(_listensockfd < 0)
        {
            lg(Fatal, "create socket error, errno: %d, errstr: %s", errno, strerror(errno));
            exit(CREAT_ERR);
        }
        lg(Info, "create socket success, listensockfd: %d", _listensockfd);

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        inet_aton(_ip.c_str(), &(local.sin_addr));

        if(bind(_listensockfd, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind socket error, errno: %d, errstr: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind socket success, listensockfd: %d", _listensockfd);

        // TCP面向连接，服务器一直等待连接的到来
        if(listen(_listensockfd, backlog) < 0)
        {
            lg(Fatal, "listen socket error, errno: %d, errstr: %s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
        lg(Info, "listen socket success, listensockfd: %d", _listensockfd);
    }

    static void* Routine(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        td->svr->Service(td->sockfd, td->clientip, td->clientport);
        delete td;
        return nullptr;
    }
    
    void Run()
    {
        lg(Info, "TcpServer is running...");
        for( ; ; )
        {
            // 获取新连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(_listensockfd, (struct sockaddr*)&client, &len);
            if(sockfd < 0)
            {
                lg(Warning, "accept error, errno: %d, errstr: %s", errno, strerror(errno));
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            lg(Info, "get a new link, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);

            // 用新连接来进行通信

            // v1 -- 单进程版本
            // Service(sockfd, clientip, clientport);
            // close(sockfd);

            // v2 -- 多进程版本
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     // child
            //     close(_listensockfd);
            //     if(fork() > 0) exit(0); // 孙进程与父进程并发
            //     Service(sockfd, clientip, clientport);
            //     close(sockfd);
            //     exit(0);
            // }
            // close(sockfd);
            // pid_t rid = waitpid(id, nullptr, 0);
            // (void)rid;

            // v3 -- 多线程版本
            ThreadData* td = new ThreadData(sockfd, clientip, clientport, this);
            pthread_t tid;
            pthread_create(&tid, nullptr, Routine, td);
        }
    }

    void Service(int sockfd, const std::string& clientip, const uint16_t& clientport)
    {
        char buffer[4096];
        while(true)
        {
            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if(n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << buffer << std::endl;
                std::string echo_string = "tcpserver echo# ";
                echo_string += buffer;

                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0)
            {
                lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else
            {
                lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
                break;
            }
        }
        
    }
private:
    int _listensockfd;
    uint16_t _port;
    std::string _ip;
};